<?php
namespace classlib\core\view;
use classlib\cache;
use classlib\core\router;
use classlib\core\config;
class html extends view_abstract {
	const IViewSubFix       = '.iview';
 	protected $template_dir = 'view/';
    protected $compile_dir	= '_compile/';
    protected $publish_dir	= '';
    protected $left_delimiter  = '<{';
    protected $right_delimiter = '}>';
    protected $caching		= false;
    protected $dirlevel   	= 1;
    protected $data;
    
    /**
     * 模板引擎的构造器初始化参数
     * @return void
     */
    public function __construct() {
    	$config  = config::load('html', false);
   	 	foreach($config as $key=>&$value) {
    		$this->$key = $value;
    	}
    	if (config::$appdir) {
    		$this->template_dir = COREDIR.'/src/'.basename(config::$appdir).'/view/';
    	}
        $this->data = array();
    }

   /**
	 * 变量赋值
	 * @param string $key
	 * @param mix    $val
	 * @return view_abstract
	 */
	public function assign($key, $val) {
		$this->data[$key] = $val;
	}
	
	/**
	 * 变量赋值引用赋值
	 * @param string $key
	 * @param mix $val
	 * @return view_abstract
	 */
	public function assign_by_ref($key, &$val) {
		$this->data[$key] = $val;
	}
	
	/**
	 * 已经分配的变量的获取 默认取所有
	 * @param string $key
	 * @return mix
	 */
	public function get_template_vars($key=false) {
		return isset($this->data[$key])? $this->data[$key]:$this->data;
	}
	
    /**
     * 解析模版引擎目录结构
     * Enter description here ...
     * @param string $tpl
     * @return string $dir
     */
    private function _hashdir(&$tpl) {
    	$dir  = md5($tpl);
        if ($this->dirlevel > 1) {
        	$len = ceil(32 / $this->dirlevel);
        	$dir = chunk_split($dir, $len, '/');
        } else {
        	$dir .= '/';
        }
        return $dir;
    }
    
 	/**
 	 * 创建目录
 	 * Enter description here ...
 	 * @param string $dirs
 	 */
    protected function mkdirs($dirs) {
		if(is_dir($dirs)) {
			return true;
		}
        mkdir($dirs, 0777, true) or die('html_view mkdirs access deny!');
        return true;
    }
    
    /**
	 * 将要输出的资料获取
	 * @param string  $tpl
	 * @param boolean $iscache
	 * @return string
	 */
	public function fetch($tpl=false) {
		$view    = $this->template_dir.$tpl;
        $cplfile = $this->compile_dir.$this->_hashdir($view).basename($view).'.compile.php';
        //重新编译,调试阶段、编译文件不存在、更新时间等
        if (!$this->_check_compile($view, $cplfile)) {
        	$this->_compile($view, $cplfile);
        }
        parent::display();
        extract($this->data);
        //展示数据
        ob_start();
        @include($cplfile);
        $html  = ob_get_contents();
        ob_end_clean();
        return $this->filter_csrf($html);
	}
	
	/**
	 * 过滤处理，对特定的Form请求添加CSRF_TOKEN
	 * @param string $html
	 * @return string
	 */
	private function filter_csrf($html) {
		$token= \classlib\web::addcsrf();
		$csrf = '<input type="hidden" value="'.$token.'" name="req_CSRFTOKEN" /></form>';
		$html = str_replace('</form>', $csrf, $html);
		return $html;
	}
	
	/**
	 * 主要是为了迎合smarty而做的处理
	 * @param string $tpl_file
	 * @param string $cache_id
	 * @param string $compile_id
	 */
	public function is_cached($tpl_file, $cache_id = null) {
		if(!$this->caching || !$this->cache) {
			return false;
		}
		$szkey = 'view@'.md5($cache_id.$tpl_file);
		$data  = $this->cache->get($szkey);
		if ($data && is_array($data)) {
			$this->data    = $data;
			$this->caching = false;
			return true;
		}
		return false;
	}
	
	/**
	 * 设置发布目录资料信息
	 * @param string $pubdir
	 */
	public function setpubdir($pubdir) {
		if (defined('OPENDEBUG') && OPENDEBUG) {
			return;
		}
		$this->publish_dir = rtrim($pubdir, '/').'/';
	}
	
    /**
     * 程序解析
     * @param 
     * (non-PHPdoc)
     * @see view_abstract::display()
     */
    public function display($tpl=false, $cacheid=null, $lifetime = -1) {
    	header('Content-Type: text/html;charset=utf-8');
        if ($this->caching && $this->cache) {
        	$szkey = 'view@'.md5($cacheid.$tpl);
        	$this->cache->set($szkey, $this->data);
        }
        $html = $this->fetch($tpl);
        //发布系统，主要用户CMS静态页建站
        if ($this->publish_dir) {
			if (empty(router::$uri)) {
				router::$uri  = 'index';		
			} 
			router::$uri .= '.html';
        	$static       = $this->publish_dir.router::$uri;
        	$this->mkdirs(dirname($static));
        	@file_put_contents($static, $html, LOCK_EX) or die('html_view publish dir write access deny');
        }
        echo $html;
    }

    /**
     * 完成编译写编译cache
     * Enter description here ...
     * @param string $tpl
     * @param string $ctpl
     */
    private function _compile($tpl, $ctpl) {
    	$files  = array($tpl);
        $buffer = file_get_contents($tpl);
        $buffer = $this->_parse_include($buffer, $files);
        //解析标签完成标签动态化
        $buffer = str_replace(array($this->left_delimiter.'=', $this->left_delimiter, 
        	$this->right_delimiter), array('<?php echo ', '<?php ', '?>'), $buffer);
        $this->mkdirs(dirname($ctpl));
        @file_put_contents($ctpl, $buffer, LOCK_EX) or die('html_view compile write access deny');
        //记录包含的所有文件信息
        @file_put_contents($ctpl.self::IViewSubFix, implode(';', $files), LOCK_EX);
    }
    
    /**
     * 递归解析include 是否包含文件信息
     * @param string $buffer
     * @param array $files
     * @return string
     */
    private function _parse_include($buffer, &$files) {
    	$incptn = '/'.preg_quote($this->left_delimiter).'[\s]*include[^\w]*([\w\.\/]+)[^\w]*'
    		.preg_quote($this->right_delimiter).'/i';
    	//解析include 完成模板替换	
    	while (preg_match_all($incptn, $buffer, $match)) {
    		for($i=0, $size=count($match[1]); $i< $size; $i++) {
    			$file   = str_replace(array('/','\\'), DIRECTORY_SEPARATOR, $this->template_dir.$match[1][$i]);
    			$data   = file_get_contents($file);
    			$buffer = str_replace($match[0][$i], $data, $buffer);
    			$files[]= $file;
    		}
    	}
		return $buffer;
    }
    
    /**
     * 验证模板文件是否改变过
     * @param string $tpl
     * @param string $ctpl
     * @return boolean
     */
    private function _check_compile($tpl, $ctpl) {
    	$iview = $ctpl.self::IViewSubFix;
    	//编译模板根本不存在
    	if (!file_exists($ctpl) || !file_exists($iview)) {
    		return false;
    	}
    	$files = file_get_contents($iview);
    	$files = explode(';', $files);
    	$mtime = filemtime($ctpl);
    	foreach ($files as $file) {
    		if (filemtime($file) > $mtime) {
    			return false;
    		}
    	}
    	return true;
    }
}